
%token <string> ID
%token <int> INT
%token <string> STRING

%token OPEN CLOSE LBRACK RBRACK

%token KW_FALSE KW_TRUE
%token KW_BEGIN KW_END
%token KW_LET KW_REC KW_IN
%token KW_IF KW_THEN KW_ELSE
%token KW_MATCH KW_WITH
%token KW_FUN KW_FUNCTION
%token KW_USE
%token KW_TO_STRING KW_WRITE
%token KW_OR KW_AND
%token KW_REF

%token SHARP DOLLAR
%token CONCAT
%token ANY OR ARROW CONS
%token LT LE EQ NE GE GT
%token AND LAND LLAND LOR LLOR KW_NOT
%token COLON COMMA SEMICOLON EOL
%token PLUS MINUS UMINUS TIMES DIVIDE
%token ASSIGN DEREF

%right ARROW
%left SEMICOLON
%right ASSIGN
%nonassoc KW_IN KW_THEN KW_AS
%left KW_ELSE
%right COMMA
%left KW_OR LOR
%left AND LAND
%nonassoc LT LE EQ NE GE GT
%left PLUS MINUS
%left TIMES DIVIDE
%right KW_NOT
%left CONCAT
%right CONS
%nonassoc DOLLAR
%nonassoc OPEN CLOSE LBRACK RBRACK
%nonassoc UMINUS KW_REF DEREF
%nonassoc PRIO

%start main
%type <Expr.global_t> main

%%

main:
  expr EOL { Expr.G_Expr $1 }
| directive { $1 }
| global_decl { $1 }
;

directive:
  SHARP KW_USE STRING EOL { Expr.G_Use $3 }
;

global_decl:
  KW_LET ID arg_list EQ expr EOL { Expr.G_Let($2,$3,$5) }
| KW_LET KW_REC ID arg_list EQ expr EOL { Expr.G_LetRec($3,$4,$6) }
;

const:
  KW_FALSE { Expr.Bool false }
| KW_TRUE { Expr.Bool true }
| INT { Expr.Int $1 }
| STRING { Expr.String $1 }
;

expr:
  const { Expr.Const $1 }
| ID { Expr.Id $1 }
| KW_NOT expr { Expr.UnaryOp(Expr.Not, $2) }
| UMINUS expr { Expr.UnaryOp(Expr.Neg, $2) }
| DOLLAR expr { Expr.UnaryOp(Expr.ToString, $2) }
| KW_REF expr { Expr.UnaryOp(Expr.Ref, $2) }
| DEREF expr { Expr.UnaryOp(Expr.Deref, $2) }
| OPEN KW_WRITE expr CLOSE { Expr.UnaryOp(Expr.Write, $3) }
| expr COMMA expr { Expr.BinaryOp(Expr.Pair, $1, $3) }
| expr CONS expr { Expr.BinaryOp(Expr.Cons, $1, $3) }
| expr CONCAT expr { Expr.BinaryOp(Expr.Concat, $1, $3) }
| expr PLUS expr { Expr.BinaryOp(Expr.Plus, $1, $3) }
| expr MINUS expr { Expr.BinaryOp(Expr.Minus, $1, $3) }
| expr TIMES expr { Expr.BinaryOp(Expr.Times, $1, $3) }
| expr DIVIDE expr { Expr.BinaryOp(Expr.Divide, $1, $3) }
| expr AND expr { Expr.BinaryOp(Expr.And, $1, $3) }
| expr KW_OR expr { Expr.BinaryOp(Expr.Or, $1, $3) }
| expr LAND expr { Expr.BinaryOp(Expr.LazyAnd, $1, $3) }
| expr LOR expr { Expr.BinaryOp(Expr.LazyOr, $1, $3) }
| expr EQ expr { Expr.BinaryOp(Expr.Eq, $1, $3) }
| expr NE expr { Expr.BinaryOp(Expr.Ne, $1, $3) }
| expr LE expr { Expr.BinaryOp(Expr.Le, $1, $3) }
| expr LT expr { Expr.BinaryOp(Expr.Lt, $1, $3) }
| expr GE expr { Expr.BinaryOp(Expr.Ge, $1, $3) }
| expr GT expr { Expr.BinaryOp(Expr.Gt, $1, $3) }
| expr ASSIGN expr { Expr.BinaryOp(Expr.Assign, $1, $3) }
| KW_FUN arg_ne_list ARROW expr { Expr.Fun($2,$4) }
| KW_FUNCTION match_ne_list { Expr.Function $2 }
| KW_BEGIN expr KW_END { $2 }
| OPEN expr_list CLOSE { Expr.Apply $2 }
| OPEN unop CLOSE {Expr.CurryUnary $2 }
| OPEN binop CLOSE { Expr.CurryBinary $2 }
| expr SEMICOLON expr { Expr.Seq($1,$3) }
| LBRACK list_content RBRACK { Expr.List $2 }
| KW_LET ID arg_list EQ expr KW_IN expr { Expr.Let($2,$3,$5,$7) }
| KW_LET KW_REC ID arg_list EQ expr KW_IN expr { Expr.LetRec($3,$4,$6,$8) }
| KW_IF expr KW_THEN expr { Expr.If($2,$4,None) }
| KW_IF expr KW_THEN expr KW_ELSE expr { Expr.If($2,$4,Some $6) }
| KW_MATCH expr KW_WITH match_ne_list { Expr.Match($2,$4) }
;

unop:
  KW_NOT { Expr.Not }
| UMINUS { Expr.Neg }
| KW_TO_STRING { Expr.ToString }
| KW_WRITE { Expr.Write }
| KW_REF { Expr.Ref }
| DEREF { Expr.Deref }
;

binop:
  COMMA { Expr.Pair }
| CONS { Expr.Cons }
| CONCAT { Expr.Concat }
| PLUS { Expr.Plus }
| MINUS { Expr.Minus }
| TIMES { Expr.Times }
| DIVIDE { Expr.Divide }
| AND { Expr.And }
| KW_OR { Expr.Or }
| LAND { Expr.LazyAnd }
| LOR { Expr.LazyOr }
| EQ { Expr.Eq }
| NE { Expr.Ne }
| LE { Expr.Le }
| LT { Expr.Lt }
| GE { Expr.Ge }
| GT { Expr.Gt }
| ASSIGN { Expr.Assign }
;

list_content:
  { [] }
| list_ne_content { $1 }
;

list_ne_content:
  expr { [$1] }
| expr list_ne_content { $1::$2 }
;

expr_list:
  { [] }
| expr_ne_list { $1 }
;

expr_ne_list:
  expr { [$1] }
| expr expr_ne_list { $1::$2 }
;

arg_list:
  { [] }
| arg_ne_list { $1 }
;

arg_ne_list:
  ID { [$1] }
| ID arg_ne_list { $1::$2 }
;

match_ne_list:
  match_rule { [$1] }
| match_rule OR match_ne_list { $1::$3 }
;

match_rule:
  pattern ARROW expr { Expr.MatchRule($1,$3) }
;

pattern:
  const { Expr.P_Const $1 }
| ANY { Expr.P_Any }
| ID { Expr.P_Id $1 }
| OPEN pattern CLOSE { $2 }
| OPEN CLOSE { Expr.P_Unit }
| pattern COMMA pattern { Expr.P_Pair($1,$3) }
| pattern CONS pattern { Expr.P_Cons($1,$3) }
| LBRACK pattern_list_content RBRACK { Expr.P_List $2 }
;

pattern_list_content:
  { [] }
| pattern_list_ne_content { $1 }
;

pattern_list_ne_content:
  pattern { [$1] }
| pattern pattern_list_ne_content { $1::$2 }
;

